package com.apes.framework.plugin.fsm;

import com.alibaba.fastjson.JSON;
import com.apes.framework.api.DomainService;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.fsm.util.FsmUtil;
import com.apes.framework.plugin.fsm.util.TaskParam;
import com.apes.framework.util.DateUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.framework.util.Tools;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;

@Service
public class FsmTaskService extends DomainService implements DisposableBean {
    protected AtomicLong counter = new AtomicLong(0);
    protected Queue<FsmStateInstanceTask> queue = new LinkedList<>();
    private Thread thread;
    private volatile boolean stop = false;

    @Autowired
    private FsmStateInstanceTaskRepository taskRepository;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private FsmTaskLogRepository logRepository;

    public FsmTaskService() {
        start();
    }

    @Override
    public void destroy() {
        stop();
    }


    /**
     * 增加一个元索
     */
    private void add(FsmStateInstanceTask data) {
        queue.add(data);
    }

    /**
     * 移除并返问队列头部的元素
     **/
    private FsmStateInstanceTask poll() {
        if (queue.isEmpty()) {
            return null;
        }
        return queue.poll();
    }

    private Thread createThread() {
        Thread thread = new Thread(() -> {
            while (!stop) {
                FsmStateInstanceTask task = poll();
                if (task != null) {
                    try {
                        run(task);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                    }
                }
            }
        });
        thread.setDaemon(true);
        thread.setName(this.getClass().getSimpleName() + counter.incrementAndGet());
        thread.start();
        return thread;
    }

    private void start() {
        if (stop || thread == null) {
            thread = createThread();
            stop = false;
        }
    }

    private void stop() {
        stop = true;
        if (thread != null) {
            thread.interrupt();
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void run(FsmStateInstanceTask task) {
        FsmTaskLog taskLog = new FsmTaskLog();
        taskLog.setTaskId(task.getId());
        try {
            transactionTemplate.execute(status -> {
                FsmUtil.execute(task.getActionExpr(), JSON.parseObject(task.getParams()));
                logRepository.save(taskLog);
                return null;
            });
        } catch (Exception e) {
            e.printStackTrace();
            transactionTemplate.execute(status -> {
                taskLog.setSuccess(false);
                taskLog.setDescription(Tools.getThrowable(e).getMessage() + "\n" + Tools.getAllStackTrace(e));
                logRepository.save(taskLog);
                return null;
            });
        }
    }

    public void process(SimpleRequest request) {
        Date maxTimestamp = DateUtil.add(new Date(), Calendar.SECOND, 5); /**单位：秒**/
        while (true) {
            List<FsmStateInstanceTask> tasks = taskRepository.findByNextTimeLessThanEqualAndFinishedIsFalseOrderByIdAsc(maxTimestamp, PageRequest.of(0, 100));
            tasks.stream().forEach(task -> {
                task.incTimes();
                if (task.getTimes() == task.getRepeat()) {
                    task.setFinished(true);
                }
                if (!task.isFinished()) {
                    TaskParam taskParam = new TaskParam();
                    ReflectUtil.copyAll(task, taskParam, "next");
                    taskParam.calc(task.getNextTime());
                    task.setNextTime(taskParam.getNext());
                }
                task = taskRepository.saveAndFlush(task);
                add(task);
            });
            if (tasks.size() < 100) break;
        }
    }

    public void test(SimpleRequest request) {
        FsmStateInstanceTask task = taskRepository.findOne((String) request.get("id"));
        if (task == null) return;
        add(task);
    }
}
